/*
 * systrace - Should be a tool to analyze system call traces but can be used
 *            to analyze sequences of generic strings
 *
 * Copyright (c) 2011 Matteo Cicuttin
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 * derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>

#include <iostream>
#include <sstream>
#include <vector>
#include <map>
#include <set>
#include "FollowAnalysis.h"


AbstractFollowAnalysis::AbstractFollowAnalysis()
{
	_allocate_spectrum = true;
	_make_spectrum = true;
	_fsetlen = 3;
}

AbstractFollowAnalysis::AbstractFollowAnalysis(map<string, int> *s)
{
	_allocate_spectrum = false;
	_make_spectrum = false;
	_spectrum = s;
	_fsetlen = 3;
}

void
AbstractFollowAnalysis::set_spectrum(map<string, int> *s)
{
	_allocate_spectrum = false;
	_make_spectrum = false;
	_spectrum = s;
}

int
AbstractFollowAnalysis::follow_length(void)
{
	return _fsetlen;
}

void
AbstractFollowAnalysis::set_follow_length(int l)
{
	_fsetlen = l;
}

int
AbstractFollowAnalysis::call_occurrencies(const string& s)
{
	if ( _spectrum->find(s) == _spectrum->end())
		return 0;
	return (*_spectrum)[s];
}

void
AbstractFollowAnalysis::count_call(const string& s)
{
	if ( _spectrum->find(s) == _spectrum->end())
		(*_spectrum)[s] = 1;
	else
		(*_spectrum)[s]++;
}

void
AbstractFollowAnalysis::run_interactive(void)
{
	while (1)
	{
		stringstream oss;
		vector<string> tokens;
		char *s, *pch;
		
		oss << "\x1B[1;33m" << module_name() << " >>\x1B[0m ";
		
		s = readline(oss.str().c_str());
		add_history(s);
		
		pch = strtok(s, " ");
		while (pch)
		{
			tokens.push_back(pch);
			pch = strtok(NULL, " ");
		}
		
		if ( tokens.size() == 0 )
			continue;
		
		if ( tokens.at(0) == "enable" )
			set_enabled(true);
		
		if ( tokens.at(0) == "disable" )
			set_enabled(false);
		
		if ( tokens.at(0) == "print" )
		{
			if (tokens.size() == 2)
				print(tokens.at(1), cout);
			else
				print_result(cout);
		}
		
		if ( tokens.at(0) == "fprint" )
		{
			if (tokens.size() == 2)
				fprint( tokens.at(1) );
			else
				cout << "Specify output file name!" << endl;
		}
		
		if ( tokens.at(0) == "length" )
		{
			if (tokens.size() == 2)
			{
				flush();
				set_follow_length( atoi(tokens.at(1).c_str()) );
			}
			else
				cout << follow_length() << endl;
		}
		
		if ( tokens.at(0) == "help" )
			help();
		
		if ( tokens.at(0) == "flush" )
			flush();
		
		if ( tokens.at(0) == "exit" )
			break;
		
		tokens.clear();
		free(s);
	}
}

void
AbstractFollowAnalysis::help(void)
{
	cout << "  \x1B[1mprint [name]\x1B[0m         print results" << endl;
	cout << "  \x1B[1mfprint <filename>\x1B[0m    print results to file" << endl;
	cout << "  \x1B[1menable\x1B[0m               enable module" << endl;
	cout << "  \x1B[1mdisable\x1B[0m              disable module" << endl;
	cout << "  \x1B[1mexit\x1B[0m                 return to parent context" << endl;
	cout << "  \x1B[1mflush\x1B[0m                flush data structures" << endl;
	cout << "  \x1B[1mlength [len]\x1B[0m         get/set length of follow set (set requires reprocessing)" << endl;
}

/***************************************************************************/
UFollowAnalysis::UFollowAnalysis(void)
{
	set_module_name("ufollow");
	_fl = 0;
}

UFollowAnalysis::UFollowAnalysis(map<string, int> *s)
	: AbstractFollowAnalysis(s)
{
	set_module_name("ufollow");
	_fl = 0;
}

void
UFollowAnalysis::print_result(ostream& os)
{
	map<string, vector< set<string> > >::iterator itor;
	for(itor = _fmap.begin(); itor != _fmap.end(); itor++)
		print( (*itor).first, os );
}

void
UFollowAnalysis::print(const string& s, ostream& os)
{
	if ( _fmap.find(s) == _fmap.end() )
	{
		cout << "There is no information for \x1B[1;31m" << s << "\x1B[0m" << endl;
		return;
	}
	
	os << "\x1B[1;31mObserved follows of " << s
		 << " (occurred " << (*_spectrum)[s] << " times):\x1B[0m" << endl;
	
	vector< set<string> > fs = _fmap[s];
	vector< set<string> >::iterator vec_itor;
	set<string>::iterator set_itor;
	int i;
	
	for(i = 0; i < fs.size(); i++)
	{
		os << "\x1B[1;32mPosition "<< i+1 << " (" << fs.at(i).size() << " items):\x1B[0m ";
		for(set_itor = fs.at(i).begin(); set_itor != fs.at(i).end(); set_itor++)
			os << *set_itor << " ";
		os << endl;
	}
}

void
UFollowAnalysis::flush(void)
{
	_follows.clear();
	_fl = 0;
	_fmap.clear();
}

void
UFollowAnalysis::add_sample(const string& s)
{
	_follows.push_back(s);
	_fl++;
	
	if ( _fl != follow_length()+1)
		return;
	
	if (_allocate_spectrum)
	{
		_spectrum = new map<string, int>();
		_allocate_spectrum = false;
	}
	
	
	if (_make_spectrum)
		count_call(s);
	
	string call = _follows.front();
	
	_follows.pop_front();
	_fl--;
	
	if ( _fmap.find(call) == _fmap.end() )
	{
		int i;
		vector< set<string> > v;
		list<string>::iterator itor;
		
		v.resize(follow_length());
		//cout << "X" << endl;
		for (i = 0, itor = _follows.begin(); i < follow_length(); i++, itor++)
		{
			//cout << "A: "<< call << " " << *itor << endl;
			v.at(i).insert( *itor );
		}
		
		_fmap[call] = v;
	}
	else
	{
		int i;
		list<string>::iterator itor;
		//cout << "Y" << endl;
		for (i = 0, itor = _follows.begin(); i < follow_length(); i++, itor++)
		{
			//cout << "B: "<< call << " " << *itor << endl;
			_fmap[call].at(i).insert( *itor );
		}
	}
}

/***************************************************************************/
SFollowAnalysis::SFollowAnalysis()
{
	set_module_name("sfollow");
	_fl = 0;
}

SFollowAnalysis::SFollowAnalysis(map<string, int> *s)
	: AbstractFollowAnalysis(s)
{
	set_module_name("sfollow");
	_fl = 0;
}

void
SFollowAnalysis::add_sample(const string& s)
{	
	_follows.push_back(s);
	_fl++;
	
	if ( _fl != follow_length()+1)
		return;
	
	if (_allocate_spectrum)
	{
		_spectrum = new map<string, int>();
		_allocate_spectrum = false;
	}

	
	if (_make_spectrum)
		count_call(s);
			
	string call = _follows.front();
	
	_follows.pop_front();
	_fl--;
	
	_fmap[call].insert(_follows);
}

void
SFollowAnalysis::print_result(ostream& os)
{
	map<string, set< list<string> > >::iterator follow_itor;
	for(follow_itor = _fmap.begin(); follow_itor != _fmap.end(); follow_itor++)
		print( (*follow_itor).first, os );
}

void
SFollowAnalysis::print(const string& s, ostream& os)
{
	if ( _fmap.find(s) == _fmap.end() )
	{
		cout << "There is no information for \x1B[1;31m" << s << "\x1B[0m" << endl;
		return;
	}
	
	os << "\x1B[1;31mObserved follow sets of " << s
	<< " (occurred " << (*_spectrum)[s] << " times):\x1B[0m" << endl;
	
	set< list<string> > ss = _fmap[s];
	set< list<string> >::iterator set_itor;
	for(set_itor = ss.begin(); set_itor != ss.end(); set_itor++)
	{
		list<string> l = (*set_itor);
		list<string>::iterator list_itor;
		os << "\t";
		for (list_itor = l.begin(); list_itor != l.end(); list_itor++)
			os << (*list_itor) << " ";
		os << endl;
	}
}


void
SFollowAnalysis::flush(void)
{
	_follows.clear();
	_fmap.clear();
}





